let obj = { a: 2 , foo1:foo1}
function foo1() : void {
    console.log(this.a); 
}
/// ============================================================================
/// ======================= this绑定之隐式绑定 ==================================
/// ============================================================================
// 隐式绑定的方式是在函数调用前指定对象（上下文对象)。如 obj.fun()
// 但其缺点是容易丢失this绑定，调用回调函数的函数可能会修改 this
// 如在函数赋值传递的过程中(如函数回调)中是this会被修改
obj.foo1();
setTimeout(obj.foo1,3000);  // 在这里回调调用时，this的对象已经丢失了。
let bar11 = obj.foo1();
bar2();  // bar2调用函数的过程中，this对象的绑定也丢失了。
// 隐式绑定会丢失this对象的原因是因为 : 函数赋值传递是引用传递，引用的是 foo 函数的调用者不会被传递。


/// ============================================================================
/// ======================= this绑定之硬绑定 ====================================
/// ============================================================================
/// 硬绑定是通过函数的call|apply方法实现
let bar1 = function() {
    foo1.call(obj);
}

bar1();
setTimeout(bar1,1000);

/// 更好的方法是硬绑定典型应用场景是创建一个包裹函数
function bind(fn:Function,object:any) : Function {
    return function() {
        return fn.call(obj,arguments);
    }
}

var bar2 = bind(foo1,obj);
bar2();
setTimeout(bar2,2000);

/// 硬绑定是一种非常常用的模式,所以ES5提供了Function.prototype.bind方法
var bar21 = foo1.bind(obj);
setTimeout(bar21,3000);


/// ============================================================================
/// ======================= this绑定之NEW绑定 ===================================
/// ============================================================================
// 在JS语言中，一定要注意一点: 构造函数只是一些 用 new 操作符时被调用的函数,并不属于某个类。new也不会实例化一个类。
// 实际上，它们甚至都不能说是一种特殊的函数类型，它们只是被new操作符调用的普通函数而已

function NormalFunction() {
    console.log("NormalFunction Call...");
    this.prop1 = "prop1";   
}


/** 
* 对于new Function() 这个语句来说，代码表达式执行以下关键的四步操作：
* 1. 创建一个他新的对象  :          let someobject = new Object();
* 2. 新对象会被执行 【原型】 连接 :  someobject.protoype.constructor = NomalFunction ...
* 3. 新对象绑定到函数调用的this  :   someobject.prop1 = "prop1";
* 4. 如果函数不返回其它对象，则new表达式的函数会自动返回这个新对象 : return someobject;
*/
// 在typescript中，不能直接new函数，必须要使用class定义类
// let o1 = new NormalFunction();
// console.log(o1);

// new绑定比隐式绑定有更高的优先级


/// ============================================================================
/// ======================= 函数绑定的优先级  ===================================
/// ============================================================================
// 优先级遵循的次序 : new绑定 > 硬绑定 > 隐式绑定
// 判断this的规则
// 1. 函数是否在new中调用，如果是的话this绑定的是新创建的对象 : var obj = new foo();
// 2. 函数是否通过call/apply硬绑定调用，如果是的话，this绑定的是call参数中指定的对象 : var bar = foo.call(obj2);
// 3. 函数是否在上下文对象中调用，如果是的话，this绑定的是那个上下文对象。
// 4. 如果以上都不是，则使用默认绑定。在严格模式下，绑定到undefined, 否则绑定到全局对象(如window)




/// ============================================================================
/// ======================= 箭头函数的绑定  =====================================
/// ============================================================================
// 箭头函数不使用this的四种标准，而是根据调用时的外层（函数或全局）作用域来决定this

function foo4() {
    return () => {
        // 注意，这里this继承自 foo4，哪个对象调用foo4，this就是谁
        console.log(this.a);
    }
}

var obj2 = {a:2,foo4:foo4};
var obj3 = {a:3};

obj2.foo4()();  // this==>obj2
let cbFun = obj2.foo4();
setTimeout(cbFun,1000);  // this==>obj2
var bar = foo4.call(obj2); // this==>obj2，注意，bar函数中的this指向已经确定使用了obj2
bar.call(obj3);         // this ==> obj2,